/*
 * FloorRun_Manager.cpp
 *
 *  Created on: Mar 7, 2012
 *      Author: ajeffery
 */

#include "FloorRun_Manager.h"
#include "defs.h"
#include "Elevator.h"
#include <native/sem.h>
#include <native/task.h>
#include <native/timer.h>
#include <native/mutex.h>
#include <native/cond.h>
#include <rtdk.h>

#include <stdlib.h>
#include <stdio.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

RT_SEM dest_count;

//Mutexes
RT_MUTEX flr_directionMutex;
RT_MUTEX flr_curFloorMutex;
RT_MUTEX flr_next_destMutex;
RT_MUTEX flr_statusMutex;
RT_MUTEX flr_dest_floorsMutex;
RT_MUTEX idle_mutex;
RT_COND idle_flag;

extern Elevator* this_lift;

FloorRun_Manager::FloorRun_Manager() {
	int err = rt_sem_create(&dest_count, 0, 0, S_FIFO);
	if (err < 0)
		perror("ERROR :: Dest_Floor variable creation failed");

	rt_mutex_create(&flr_directionMutex, NULL);
	rt_mutex_create(&flr_curFloorMutex, NULL);
	rt_mutex_create(&flr_next_destMutex, NULL);
	rt_mutex_create(&flr_statusMutex, NULL);
	rt_mutex_create(&flr_dest_floorsMutex, NULL);
	rt_mutex_create(&idle_mutex, NULL);

	rt_cond_create(&idle_flag, NULL);

	this->cur_floor=this_lift->getCur_floor();
	this->cur_status=this_lift->getStatus();
	this->direction=this_lift->getDirection();
	this->next_dest=this_lift->getDest_floor();
	this->reached_dest=0;
}

FloorRun_Manager::~FloorRun_Manager() {
	rt_mutex_delete(&flr_directionMutex);
	rt_mutex_delete(&flr_curFloorMutex);
	rt_mutex_delete(&flr_next_destMutex);
	rt_mutex_delete(&flr_statusMutex);
	rt_mutex_delete(&flr_dest_floorsMutex);
	rt_mutex_delete(&idle_mutex);

	rt_cond_delete(&idle_flag);
}

void FloorRun_Manager::open_door(){
	rt_task_sleep(3*SLEEP1S);
	set_status(CLOSING);

}

void FloorRun_Manager::close_door(){
	rt_task_sleep(2*SLEEP1S);
	set_status(SPOOLING);
}

void FloorRun_Manager::next_floor(){
	rt_task_sleep(2*SLEEP1S);
	switch(get_dir()){
	case(UP):
		if(get_cur_floor()==(MAX_FLOORS -1)){
			set_status(SPOOLING);
		}
		else
			set_cur_floor(get_cur_floor() + 1);
		break;
	case(DOWN):
		if(get_cur_floor()==0){
			set_status(SPOOLING);
		}
		else
			set_cur_floor(get_cur_floor() - 1);
		break;
	}
}

void FloorRun_Manager::operate(){

	switch(get_status()){

	case(MOVING):
		if ( get_dest() == get_cur_floor()  ){
			set_status(OPENING);
			set_reached_dest();
		}
		else
		{
			send_timestamp(NEXT_FLOOR);
			next_floor();
		}
		break;

	case(OPENING):
		send_timestamp(ARRIVAL);
		open_door();
		break;

	case(CLOSING):
		close_door();
		break;

	case(SPOOLING):
		rt_mutex_acquire(&idle_mutex, TM_INFINITE);
		rt_printf("Waiting\n");
		rt_cond_wait(&idle_flag, &idle_mutex,TM_INFINITE);
		rt_printf("Done Waiting\n");
		rt_mutex_release(&idle_mutex);
		set_status(MOVING);
		send_timestamp(DEPARTED);
		break;
	}

	this->operate();  //recursive call to progress through states
}

int8_t FloorRun_Manager::get_cur_floor()
{
	int8_t temp=0;
	rt_mutex_acquire(&flr_curFloorMutex,TM_INFINITE);
	temp=cur_floor;
	rt_mutex_release(&flr_curFloorMutex);
	return temp;
}

int8_t FloorRun_Manager::get_status()
{
	int8_t temp=0;
	rt_mutex_acquire(&flr_statusMutex,TM_INFINITE);
	temp=cur_status;
	rt_mutex_release(&flr_statusMutex);
	return temp;
}

int8_t FloorRun_Manager::get_dir()
{
	int8_t temp=0;
	rt_mutex_acquire(&flr_directionMutex,TM_INFINITE);
	temp=direction;
	rt_mutex_release(&flr_directionMutex);
	return temp;
}

int8_t FloorRun_Manager::get_dest()
{
	int8_t temp=0;
	rt_mutex_acquire(&flr_next_destMutex,TM_INFINITE);
	temp=next_dest;
	rt_mutex_release(&flr_next_destMutex);
	return temp;
}

void FloorRun_Manager::set_cur_floor(int8_t floor)
{
	rt_mutex_acquire(&flr_curFloorMutex,TM_INFINITE);
	cur_floor=floor;
	rt_mutex_release(&flr_curFloorMutex);
}

void FloorRun_Manager::set_status(int8_t status)
{
	rt_mutex_acquire(&flr_statusMutex,TM_INFINITE);
	cur_status=status;
	rt_mutex_release(&flr_statusMutex);
}

void FloorRun_Manager::set_dir(int8_t dir)
{
	rt_mutex_acquire(&flr_directionMutex,TM_INFINITE);
	direction = dir;
	rt_mutex_release(&flr_directionMutex);
}

void FloorRun_Manager::set_dest(int8_t floor)
{
	rt_mutex_acquire(&flr_next_destMutex,TM_INFINITE);
	next_dest = floor;
	reached_dest = 0;
	rt_mutex_release(&flr_next_destMutex);

	rt_mutex_acquire(&idle_mutex, TM_INFINITE);
	rt_cond_broadcast(&idle_flag);
	rt_mutex_release(&idle_mutex);
}

int FloorRun_Manager::check_reached_dest(){
	int result = 0;
	rt_mutex_acquire(&flr_next_destMutex,TM_INFINITE);
	result = reached_dest;
	rt_mutex_release(&flr_next_destMutex);
	return result;
}

void FloorRun_Manager::set_reached_dest(){
	rt_mutex_acquire(&flr_next_destMutex,TM_INFINITE);
	reached_dest = 1;
	rt_mutex_release(&flr_next_destMutex);
}

int FloorRun_Manager::init_test_socket(int test_socket){
	return this->test_sock = test_socket;
}

void FloorRun_Manager::send_timestamp(int event_type){
	if(test_sock <= 0)
		return;

	char oBuffer[BUF_SIZE];
	test_stamp_t oStamp;

	memset(oBuffer,0, sizeof(oBuffer));
	oStamp.type = EVENT_MSG;
	oStamp.event = event_type;
	oStamp.car = this_lift->getCar_num();
	oStamp.floor = get_cur_floor();
	oStamp.timestamp = rt_timer_read() / 1000000;

	oStamp.timestamp = htonl(oStamp.timestamp);

	memcpy(oBuffer, (void*) &oStamp, sizeof(oStamp));

	int retval = write(test_sock, oBuffer, BUF_SIZE);
	if (retval < 0)
		perror("ERROR:: guiSocket write Fail");
}
